/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { ArkFile, ts } from "arkanalyzer";
import { Rule } from "../../model/Rule";
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Defects, IssueReport } from "../../model/Defects";
import {
  FileMatcher,
  MatcherCallback,
  MatcherTypes,
} from "../../matcher/Matchers";
import { RuleListUtil } from "../../utils/common/DefectsList";

interface MessageInfo {
  return: string;
  break: string;
  throw: string;
  continue: string;
}

interface Violation {
  line: number;
  character: number;
  endCol: number;  // 新增结束列字段
  type: MessageType;
  filePath?: string;
}
enum MessageType {
  return = "return",
  break = "break",
  throw = "throw",
  continue = "continue",
}

export class NoUnsafeFinallyCheck implements BaseChecker {
  public issues: IssueReport[] = [];
  private messages: MessageInfo = {
    return: "Unsafe usage of ReturnStatement",
    break: "Unsafe usage of BreakStatement",
    throw: "Unsafe usage of ThrowStatement",
    continue: "Unsafe usage of ContinueStatement",
  };
  public rule: Rule;
  public defects: Defects[] = [];

  public metaData: BaseMetaData = {
    severity: 3,
    ruleDocPath: "docs/no-unsafe-finally-check.md",
    description: "Unsafe usage of ReturnStatement",
  };

  private fileMatcher: FileMatcher = {
    matcherType: MatcherTypes.FILE,
  };

  public registerMatchers(): MatcherCallback[] {
    const fileMatcher: MatcherCallback = {
      matcher: this.fileMatcher,
      callback: this.check,
    };
    return [fileMatcher];
  }

  public check = (target: ArkFile) => {
    if (target instanceof ArkFile) {
      let code = target.getCode();
      const myInvalidPositions = this.checkNoUnsafeFinally(code);
      myInvalidPositions.forEach((pos) => {
        if (target.getFilePath() !== undefined) {
          pos.filePath = target.getFilePath();
        }

        this.addIssueReport(pos);
      });
    }
  };

  public checkNoUnsafeFinally(sourceCode: string): Violation[] {
    // 解析代码段为AST树对象
    const sourceFile = ts.createSourceFile(
      "temp.ts",
      sourceCode,
      ts.ScriptTarget.Latest,
      true
    );
    const violations: Violation[] = [];
    // 判断为函数声明、函数表达式、箭头函数、箭头函数表达式、类声明
    function isFunctionOrClassNode(node: ts.Node): boolean {
      return (
        ts.isFunctionDeclaration(node) ||
        ts.isFunctionExpression(node) ||
        ts.isArrowFunction(node) ||
        ts.isClassDeclaration(node)
      );
    }
    // 判断为循环节点
    function isLoopNode(node: ts.Node): boolean {
      return (
        ts.isDoStatement(node) ||
        ts.isWhileStatement(node) ||
        ts.isForOfStatement(node) ||
        ts.isForInStatement(node) ||
        ts.isForStatement(node)
      );
    }
    // 判断为switch...case节点
    function isSwitchNode(node: ts.Node): boolean {
      return ts.isSwitchStatement(node);
    }
    // 递归检查finally代码块
    function checkNode(node: ts.Node) {
      let valid = false;
      let caseType: string = "break";
      if (ts.isLabeledStatement(node)) {
        const label = node.label.text;
        if (ts.isWhileStatement(node.statement)) {
          const whileStatement = node.statement as ts.WhileStatement;
          const tryStatement = whileStatement.statement;
          if (ts.isTryStatement(tryStatement)) {
            const finallyBlock = tryStatement.finallyBlock;
            if (finallyBlock) {
              const switchStatement = finallyBlock.statements.find((statement) => ts.isSwitchStatement(statement)) as ts.SwitchStatement;
              // 在 `switch` 语句中查找 `break` 语句，并获取其标签
              let breakLabel: string | undefined;
              const caseBlock = switchStatement.caseBlock;
              if (caseBlock) {
                ts.forEachChild(caseBlock, (child) => {
                  if (ts.isCaseClause(child)) {
                    ts.forEachChild(child, (childNode) => {
                      if (ts.isBreakStatement(childNode) && childNode.label) {
                        breakLabel = childNode.label.text;
                        if (breakLabel === label) {
                          valid = true;
                        }
                      } else if (ts.isContinueStatement(childNode)) {
                        valid = true;
                        caseType = "continue";
                      }
                      if (valid) {

                        const nodeStart = childNode.getStart();
                        const nodeEnd = childNode.getEnd();  // 获取整个语句的结束位置
                        const { line, character } = sourceFile.getLineAndCharacterOfPosition(nodeStart);
                        const { character: endChar } = sourceFile.getLineAndCharacterOfPosition(nodeEnd);
                        violations.push({
                          line: line + 1,
                          character: character + 1,
                          endCol: endChar + 1,
                          type:
                            caseType === "continue"
                              ? MessageType.continue
                              : MessageType.break,
                        });
                      }
                    });
                  }
                });
              }
            }
          }
        }
        if (ts.isSwitchStatement(node.statement)) {
          const switchStatement = node.statement as ts.SwitchStatement;
          const tryStatement = switchStatement.caseBlock;
          if (tryStatement) {
            ts.forEachChild(tryStatement, (child) => {
              if (ts.isCaseClause(child)) {
                ts.forEachChild(child, (childNode) => {
                  if (ts.isTryStatement(childNode)) {
                    const finallyBlock = childNode.finallyBlock;
                    if (finallyBlock) {
                      const switchStatement = finallyBlock.statements.find((statement) => ts.isSwitchStatement(statement)) as ts.SwitchStatement;
                      // 在 `switch` 语句中查找 `break` 语句，并获取其标签
                      let breakLabel: string | undefined;
                      const caseBlock = switchStatement.caseBlock;
                      if (caseBlock) {
                        ts.forEachChild(caseBlock, (child) => {
                          if (ts.isCaseClause(child)) {
                            ts.forEachChild(child, (childNode) => {
                              if (
                                ts.isBreakStatement(childNode) &&
                                childNode.label
                              ) {
                                breakLabel = childNode.label.text;
                                if (breakLabel === label) {
                                  valid = true;
                                  caseType = "break";
                                  if (valid) {
                                    const nodeStart = childNode.getStart();
                                    const nodeEnd = childNode.getEnd();  // 获取整个语句的结束位置
                                    const { line, character } = sourceFile.getLineAndCharacterOfPosition(nodeStart);
                                    const { character: endChar } = sourceFile.getLineAndCharacterOfPosition(nodeEnd);
                                    violations.push({
                                      line: line + 1,
                                      character: character + 1,
                                      endCol: endChar + 1,
                                      type: MessageType.break,
                                    });
                                  }
                                }
                              }
                            });
                          }
                        });
                      }
                    }
                  }
                });
              }
            });
          }
        }
      } else {
        if (ts.isTryStatement(node)) {
          const finallyBlock = node?.finallyBlock;
          if (finallyBlock) {
            ts.forEachChild(finallyBlock, (child) => {
              const nodeStart = child.getStart();
              const nodeEnd = child.getEnd();  // 获取整个语句的结束位置
              const { line, character } = sourceFile.getLineAndCharacterOfPosition(nodeStart);
              const { character: endChar } = sourceFile.getLineAndCharacterOfPosition(nodeEnd);
              let currentNode: ts.Node | undefined = child;
              let skipCheck = false;
              if (ts.isReturnStatement(child) || ts.isThrowStatement(child)) {
                if (isFunctionOrClassNode(currentNode)) {
                  skipCheck = true;
                }
                if (!skipCheck) {
                  violations.push({
                    line: line + 1,
                    character: character + 1,
                    endCol: endChar + 1,
                    type: ts.isReturnStatement(child)
                      ? MessageType.return
                      : MessageType.throw,
                  });
                }
              } else if (ts.isBreakStatement(child)) {
                if (
                  isFunctionOrClassNode(currentNode) ||
                  isLoopNode(currentNode)
                ) {
                  skipCheck = true;
                }
                if (!skipCheck) {
                  violations.push({
                    line: line + 1,
                    character: character + 1,
                    endCol: endChar + 1,
                    type: MessageType.break,
                  });
                }
              } else if (ts.isContinueStatement(child)) {
                if (
                  isFunctionOrClassNode(currentNode) ||
                  isLoopNode(currentNode) ||
                  isSwitchNode(currentNode)
                ) {
                  skipCheck = true;
                }
                if (!skipCheck) {
                  violations.push({
                    line: line + 1,
                    character: character + 1,
                    endCol: endChar + 1,
                    type: MessageType.break,
                  });
                }
              } else {
                // 判断if代码块中 是否包含return节点
                function checkReturnInBlock(
                  statement: ts.Statement | undefined
                ) {
                  if (statement) {
                    ts.forEachChild(statement, (chil) => {
                      const { line: l, character: chara } =
                        sourceFile.getLineAndCharacterOfPosition(
                          chil.getStart()
                        );
                      if (ts.isReturnStatement(chil)) {
                        violations.push({
                          line: l + 1,
                          character: chara + 1,
                          endCol: endChar + 1,
                          type: MessageType.return,
                        });
                      }
                    });
                  }
                }
                if (isFunctionOrClassNode(currentNode)) {
                  skipCheck = true;
                }
                if (!skipCheck) {
                  if (ts.isIfStatement(currentNode)) {
                    const thenStatement = currentNode.thenStatement;
                    const elseStatement = currentNode.elseStatement;
                    checkReturnInBlock(thenStatement);
                    checkReturnInBlock(elseStatement);

                    // 检查是否是 if-elseif-else 结构
                    if (elseStatement && ts.isIfStatement(elseStatement)) {
                      checkReturnInBlock(elseStatement.thenStatement);
                      checkReturnInBlock(elseStatement.elseStatement);
                    }
                  }
                }
              }
            });
          }
        }
      }
      ts.forEachChild(node, checkNode);
    }
    checkNode(sourceFile);
    return violations;
  }

  private addIssueReport(pos: Violation) {
    this.metaData.description = this.messages[pos.type];
    const severity = this.rule.alert ?? this.metaData.severity;
    if (pos.filePath == undefined) pos.filePath = "";
    const defect = new Defects(pos.line, pos.character, pos.endCol, this.metaData.description, severity,
      this.rule.ruleId, pos.filePath, this.metaData.ruleDocPath, true, false, false);
    this.issues.push(new IssueReport(defect, undefined));
    RuleListUtil.push(defect);
  }
}
